﻿
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;

static public class Globals
{
    public static int SIMULATION_TIME = 500;    // simulation duration (steps)
    public static int NUM_PROTEIN = 0;          // current amount of protein in the system
    public static int NUM_MRNA = 0;             // current amount of mRNA in the system


    public static int MAX_NUM_PROTEIN = 0;
    public static int MIN_NUM_PROTEIN = 0;
    public static int MAX_NUM_MRNA = 0;
    public static int MIN_NUM_MRNA = 0;

    public static int TRANSCRIPTION_MODEL = 1;  // which transcription model to use

    public static string LAST_RUN_SIMULATION_NAME = String.Empty;  // Which simulation results are currently displayed on the chart?
    public static bool LIMIT_BINDING_SITE_CAPACITY = true;
}


public enum ProteinType { Repressor, Activator, None };
public enum PromoterType { Constitutive, Activated };       // Activated promoter requires activators for transcription to take place.



// Signle protein entity (molecule).
public class Protein
{
    public int BirthTime = 0;
    public bool Alive = true;

    public bool Bound = false;      // Is this protein bound to a binding site (operator)?
    public ProteinType Type = ProteinType.None;
}


// Single mRNA entity (molecule).
public class MRNA
{
    public int BirthTime = 0;
    public bool Alive = true;
}


// Protein coding region.
public class GeneExpression
{
    // The amount of mRNA units (molecules) transcribed per unit time
    public int TranscriptionRate = 10;

    // The amount or protein units (molecules) produced per unit time
    public int TranslationRate = 1;

    // Percentage of mRNA degraded per unit time.
    public double mRNADegradationPercent = 0.025;

    // Percentage of protein degraded per unit time.
    public double ProteinDegradationPercent = 0.5;

    // Delay between transcription and translation
    public int TrDelay = 0;
   
    // Promoter which regulates the transcription.
    public Promoter GenePromoter;

    // Dissociation constant (Hill equation) for activator
    public int Ka = 1;

    // Dissociation constant (Hill equation) for repressor
    public int Kr = 1;



    // Constitutive transcription.
    // NOTE: this model DOES NOT require activators for transcription! Activators increase transcription rate, but are not necessary to induce transcription!
    private void ConstitutiveTranscription(ref List<MRNA> mRNAs, int current_time)
    {
        // Repression has priority over activation - any bound repressor will invalidate bound activators.
        //if (GenePromoter.RepressionStrength > 0) { GenePromoter.ActivationStrength = 0; }
        if (GenePromoter.RepressionStrength > GenePromoter.RepressionThreshold) { GenePromoter.ActivationStrength = 0; }
        
        int TRANSCRIPTION_RATE = 0;

            /// Model 1 ///
            if (Globals.TRANSCRIPTION_MODEL == 1)
            {
                double tr1 = ((double)this.TranscriptionRate + (double)GenePromoter.ActivationStrength);
                double tr2 = tr1 * (double)GenePromoter.Strength;
                TRANSCRIPTION_RATE = (int)Math.Round(tr2 / ((double)GenePromoter.RepressionStrength + 1.0d));

                //int MAX_TRANSCRIPTION_RATE = GenePromoter.MaxTranscriptionRate;
                //if (TRANSCRIPTION_RATE > MAX_TRANSCRIPTION_RATE) { TRANSCRIPTION_RATE = MAX_TRANSCRIPTION_RATE; }
            }

            /// Model 2 ///
            if (Globals.TRANSCRIPTION_MODEL == 2)
            {
                int dif = (int)Math.Abs((double)GenePromoter.ActivationStrength - (double)GenePromoter.RepressionStrength);             // Take both activation and repression into account.
                int tr_rate = (int)Math.Round(((double)this.TranscriptionRate * (double)this.Kr) / ((double)this.Kr + (double)dif));

                if (tr_rate < 0) { tr_rate = 0; }
                TRANSCRIPTION_RATE = GenePromoter.Strength * tr_rate;
            }

        
        for (int i = 0; i < TRANSCRIPTION_RATE; i++)
        {
            MRNA m = new MRNA();
            m.Alive = true;
            m.BirthTime = current_time;
            mRNAs.Add(m);
        }
    }



    // Activated transcription.
    // NOTE: this model REQUIRES activators for transcription! No activators bound -> no transcription.
    private void ActivatedTranscription(ref List<MRNA> mRNAs, int current_time)
    {
        // mRNA generation ONLY when activator is present.
        //if (GenePromoter.ActivationStrength > 0)
        if (GenePromoter.ActivationStrength > GenePromoter.ActivationThreshold)
        {     
            // Repression has priority over activation - bound repressor (at least RepressionThreshold amount) will invalidate bound activators.
            if (GenePromoter.RepressionStrength > GenePromoter.RepressionThreshold) { GenePromoter.ActivationStrength = 0; }

            int TRANSCRIPTION_RATE = 0;

                /// Model 1 ///
                if (Globals.TRANSCRIPTION_MODEL == 1)
                {
                    double tr1 = ((double)this.TranscriptionRate + (double)GenePromoter.ActivationStrength);
                    double tr2 = tr1 * (double)GenePromoter.Strength;
                    TRANSCRIPTION_RATE = (int)Math.Round(tr2 / ((double)GenePromoter.RepressionStrength + 1.0d));

                    //int MAX_TRANSCRIPTION_RATE = GenePromoter.MaxTranscriptionRate;
                    //if (TRANSCRIPTION_RATE > MAX_TRANSCRIPTION_RATE) { TRANSCRIPTION_RATE = MAX_TRANSCRIPTION_RATE; }
                }

                /// Model 2 ///
                if (Globals.TRANSCRIPTION_MODEL == 2)
                {
                    int f_act = (int)Math.Round(((double)this.TranscriptionRate * (double)GenePromoter.ActivationStrength) / ((double)this.Ka + (double)GenePromoter.ActivationStrength));  // no activator -> no transcription
                    int f_rep = (int)Math.Round(((double)this.TranscriptionRate * (double)GenePromoter.RepressionStrength) / ((double)this.Kr + (double)GenePromoter.RepressionStrength));
                    //int f_rep = (int)Math.Round( ((double)this.TranscriptionRate * (double)this.Kr) / ((double)this.Kr + (double)GenePromoter.RepressionStrength) );

                    int tr_rate = (int)Math.Round((double)f_act / (1.0d + (double)f_rep));
                    if (tr_rate < 0) { tr_rate = 0; }
                    TRANSCRIPTION_RATE = GenePromoter.Strength * tr_rate;
                }

            for (int i = 0; i < TRANSCRIPTION_RATE; i++)            
            {
                MRNA m = new MRNA();
                m.Alive = true;
                m.BirthTime = current_time;
                mRNAs.Add(m);
            }
        }
    }



    // Transcription - mRNA generation. 
    // Transcription can be either enabled or not (one repressor bound to its site is enough to disable transcription), no intermediate values possible.
    public void Transcribe(ref List<MRNA> mRNAs, int current_time)
    {
        if (GenePromoter.Enabled)
        {
            int RepressionStrength = 0;
            int ActStrength = 0;

            // Calculate repression strength.            
            if (GenePromoter.RepressorBindSitesList != null)
            {                
                for (int i = 0; i < GenePromoter.RepressorBindSitesList.Count; i++)
                {
                    RepressionStrength += GenePromoter.RepressorBindSitesList[i].AmountOfBoundRepressor;
                }                
            }
           

            // Calculate activation strength. 
            if (GenePromoter.ActivatorBindSitesList != null)
            {
                for (int i = 0; i < GenePromoter.ActivatorBindSitesList.Count; i++)
                {
                    ActStrength += GenePromoter.ActivatorBindSitesList[i].AmountOfBoundActivator;
                }
            }


            if (GenePromoter.BindSitesList != null)
            {
                for (int i = 0; i < GenePromoter.BindSitesList.Count; i++)
                {
                    for (int j = 0; j < GenePromoter.BindSitesList[i].BoundProtein.Count; j++)
                    {
                        if (GenePromoter.BindSitesList[i].BoundProtein[j].Type == ProteinType.Repressor) { RepressionStrength++; }
                        if (GenePromoter.BindSitesList[i].BoundProtein[j].Type == ProteinType.Activator) { ActStrength++; }
                    }
                }
            }

            GenePromoter.RepressionStrength = RepressionStrength;
            GenePromoter.ActivationStrength = ActStrength;
            


            // If transcription is enabled, generate mRNA.
            if (GenePromoter.Active)
            {
                if (GenePromoter.Type == PromoterType.Constitutive)
                {
                    ConstitutiveTranscription(ref mRNAs, current_time);
                }
                else
                if (GenePromoter.Type == PromoterType.Activated)
                {
                    ActivatedTranscription(ref mRNAs, current_time);
                }
            }
        }
    }


    // Translation - protein production from mRNA.
    public void Translate(ref List<Protein> proteins, ref List<MRNA> mRNAs, int current_time)
    {
        //for (int i = 0; i < mRNAs.Count - (int)(mRNAs.Count*0.15); i++)
        for (int i = 0; i < mRNAs.Count; i++)
        {
            if (mRNAs[i].Alive) // Translation only occurs for alive (active) mRNA.
            {
                if ((current_time - mRNAs[i].BirthTime) >= this.TrDelay)  // Take transcription-translation delay into account.
                {
                    for (int j = 0; j < this.TranslationRate; j++)
                    {
                        Protein p = new Protein();
                        p.Alive = true;
                        p.BirthTime = current_time;

                        proteins.Add(p);
                    }
                }
            }
        }
    }


    // mRNA degradation.
    public void DegradeMRNA(ref List<MRNA> mRNAs)
    {
        if (mRNADegradationPercent > 1) { mRNADegradationPercent = 1; }

        int mrnad = (int)Math.Ceiling((double)mRNAs.Count * (double)this.mRNADegradationPercent);         
        //int mrnad = (int)Math.Round((double)mRNAs.Count * (double)this.mRNADegradationPercent);                   

        for (int i = 0; i < mrnad; i++)
        {
            if (mRNAs[i].Alive)
                mRNAs[i].Alive = false;
        }

        ClearDeadMRNA(ref mRNAs);
    }


    // Protein degradation.
    public void DegradeProtein(ref List<Protein> proteins)
    {
        if (ProteinDegradationPercent > 1) { ProteinDegradationPercent = 1; }

        int protd = (int)Math.Ceiling((double)proteins.Count * (double)this.ProteinDegradationPercent);     
        //int protd = (int)Math.Round((double)proteins.Count * (double)this.ProteinDegradationPercent);
        

        for (int i = 0; i < protd; i++)
        {
            if (proteins[i].Alive)
                proteins[i].Alive = false;
        }

        ClearDeadProtein(ref proteins);
    }


    // Update mRNA - remove dead molecules.
    public void ClearDeadMRNA(ref List<MRNA> mRNAs)
    {
        List<MRNA> copy = new List<MRNA>();        // Copy of alive MRNA (NOTE, non-optimal ... should probably use delegates).

        for (int i = 0; i < mRNAs.Count; i++)
        {
            if (mRNAs[i].Alive) copy.Add(mRNAs[i]);
        }

        mRNAs.Clear();
        mRNAs = copy;
    }

    // Update protein - remove dead molecules.
    public void ClearDeadProtein(ref List<Protein> proteins)
    {
        List<Protein> copy = new List<Protein>();        // Copy of alive protein (NOTE, non-optimal ... should probably use delegates).

        for (int i = 0; i < proteins.Count; i++)
        {
            if (proteins[i].Alive) copy.Add(proteins[i]);            
        }

        proteins.Clear();
        proteins = copy;
    }


    // Transcription & Translation (mRNA & mRNA degradation -> Protein & Protein degradation)
    public void Expression(ref List<MRNA> mRNAs, ref List<Protein> proteins, int current_time)
    {
        this.Transcribe(ref mRNAs, current_time);
        this.DegradeMRNA(ref mRNAs);
        this.Translate(ref proteins, ref mRNAs, current_time);
        this.DegradeProtein(ref proteins);
    }

}


public class Promoter
{
    public bool Enabled = true;   // Enabled promoter = transcription enabled. Overrides Active property. For directly turning promoter on or off!
    public bool Active = true;    // Active promoter = transcription enabled. Do not use within simulation!
    public int Strength = 1;      // Promoter strength. Multiplied with the transcription rate. Activators increase this value, repressors decrease it.    

    public int MaxTranscriptionRate = 100;    

    public int RepressionStrength = 0;  // How much transcription is repressed - sum of amount of bound repressors.
    public int ActivationStrength = 0;  // How much transcription is activated - sum of amount of bound activators.

    public int ActivationThreshold = 0; // Activation may occur if ActivationStrength > ActivationThreshold
    public int RepressionThreshold = 0; // Repression may occur if RepressionStrength > RepressionThreshold - i.e. minimal amount of repressor required for repression to take place

    // Repressor binding sites for this promoter.
    public List<RepressorBindingSite> RepressorBindSitesList = null;

    // Activator binding sites for this promoter.
    public List<ActivatorBindingSite> ActivatorBindSitesList = null;

    // General binding sites for this promoter.
    public List<BindingSite> BindSitesList = null;

    // Promoter type.
    public PromoterType Type = PromoterType.Constitutive;


    // Limits amount of bound transcription factor protein to binding site's MaxCapacity.
    public void LimitAmountOfBoundTF()
    {
        if (this.RepressorBindSitesList != null)
        {
            for (int i = 0; i < this.RepressorBindSitesList.Count; i++)
            {
                if (this.RepressorBindSitesList[i].AmountOfBoundRepressor > this.RepressorBindSitesList[i].MaxCapacity)
                {
                    this.RepressorBindSitesList[i].AmountOfBoundRepressor = this.RepressorBindSitesList[i].MaxCapacity;
                }
            }
        }

        if (this.ActivatorBindSitesList != null)
        {
            for (int i = 0; i < this.ActivatorBindSitesList.Count; i++)
            {
                if (this.ActivatorBindSitesList[i].AmountOfBoundActivator > this.ActivatorBindSitesList[i].MaxCapacity)
                {
                    this.ActivatorBindSitesList[i].AmountOfBoundActivator = this.ActivatorBindSitesList[i].MaxCapacity;
                }
            }
        }
    }
}


public class RepressorBindingSite
{
    public int AmountOfBoundRepressor = 0;      // Amount of repressor bound to this site (affecting transcription rate).
    public int MaxCapacity = 30;                // Max amount of repressor (molecules) that can bind to this site.
}


public class ActivatorBindingSite
{
    public int AmountOfBoundActivator = 0;      // Amount of activator bound to this site (affecting transcription rate).
    public int MaxCapacity = 30;                // Max amount of activator (molecules) that can bind to this site.
}


public class BindingSite                        // General binding site - both repressor and activators can bind here (e.g. TAL+activation domain or TAL+repression domain).
{
    public List<Protein> BoundProtein = null;
    public int MaxCapacity = 30;
}


static public class Interaction
{
    /*static public void BindTranscriptionFactor(ref List<Protein> TF, ref List<BindingSite> target_sites)
    {
        for (int i = 0; i < TF.Count; i++)
        {
            if (!TF[i].Bound && TF[i].Alive)
            {
                // Find freeset binding site (least bound protein).
                BindingSite site = null;                
                int minBound = int.MaxValue;
                int minIndex = 0;
                for (int s = 0; s < target_sites.Count; s++)
                {
                    if (target_sites[s].AmountOfBoundProtein < minBound &&
                        target_sites[s].AmountOfBoundProtein < target_sites[s].MaxCapacity)
                    {
                        site = target_sites[s];
                        minBound = target_sites[s].AmountOfBoundProtein;
                        minIndex = s;
                    }
                }


                if (site.AmountOfBoundProtein < site.MaxCapacity)
                {
                    site.AmountOfBoundProtein++;
                    TF[i].Bound = true;
                }

                //target_sites[minIndex] = site;
            }
        }
    }*/
}